package com.googlecode.lohr.tools.language;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.dom.Initializer;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.PackageDeclaration;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.TypeDeclaration;

import lohr.lang.dom.AbstractDOMVisitor;
import lohr.lang.dom.AccessModifier;
import lohr.lang.dom.Arguments;
import lohr.lang.dom.BooleanLiteral;
import lohr.lang.dom.ClosureBody;
import lohr.lang.dom.ClosureLiteral;
import lohr.lang.dom.ClosureParameterDeclaration;
import lohr.lang.dom.CompilationUnit;
import lohr.lang.dom.DOMVisitor;
import lohr.lang.dom.Expression;
import lohr.lang.dom.IdentifierName;
import lohr.lang.dom.Import;
import lohr.lang.dom.Imports;
import lohr.lang.dom.Literal;
import lohr.lang.dom.LohrElement;
import lohr.lang.dom.MethodInvocation;
import lohr.lang.dom.NaturalLiteral;
import lohr.lang.dom.PackageName;
import lohr.lang.dom.ReferenceExpression;
import lohr.lang.dom.SlotDefinition;
import lohr.lang.dom.StringLiteral;
import lohr.lang.dom.TypeParameterDeclaration;
import lohr.lang.dom.TypeReference;

/**
 * 
 * 
 * @see LohrCompiler
 * 
 * @author ted.stockwell
 */
@SuppressWarnings("unchecked")
public class DefaultJavaGenerator extends AbstractDOMVisitor implements JavaGenerator{
	
	LohrCompiler _lohrCompiler;
	lohr.lang.dom.CompilationUnit _lohrUnit;
	ArrayList<org.eclipse.jdt.core.dom.CompilationUnit> _javaUnits= new ArrayList<org.eclipse.jdt.core.dom.CompilationUnit>();
	
	/**
	 * The main java code file associated with the lohr module to be compiled.
	 */
	org.eclipse.jdt.core.dom.CompilationUnit _moduleUnit;
	
	/**
	 * Ancillary files associated with the lohr module, indexed by file name 
	 */
	Collection<org.eclipse.jdt.core.dom.CompilationUnit> _otherUnits= new ArrayList<org.eclipse.jdt.core.dom.CompilationUnit>();
	
	AST _ast;
	
	public DefaultJavaGenerator() {
	}
	
	/**
	 * Constructor used internally when compiling.
	 * @param lohrCompiler
	 */
	protected DefaultJavaGenerator(LohrCompiler lohrCompiler) {
		_lohrCompiler= lohrCompiler;
		_lohrUnit= lohrCompiler.getSource();
		_lohrUnit.accept(this);
		_ast= lohrCompiler.getAST();
	}

	public void compile(LohrCompiler lohrCompiler) {
		new DefaultJavaGenerator(lohrCompiler);
	}

	public boolean visit(CompilationUnit compilationUnit) {
		
		_moduleUnit= _ast.newCompilationUnit();
		
		// set package name
		PackageName lohrPackage= compilationUnit.getPackageName();
		PackageDeclaration packageDeclaration = _ast.newPackageDeclaration();
		Name packageName= _ast.newName(lohrPackage.getFullName());
		packageDeclaration.setName(packageName);
		_moduleUnit.setPackage(packageDeclaration);
		
		// set imports
		List<Import> lohrImports= compilationUnit.getImports().getImports();
		for (Import lohrImport:lohrImports) {
			ImportDeclaration importDeclaration = _ast.newImportDeclaration();
			Name importName= _ast.newName(lohrImport.getPackageName().getFullName());
			importDeclaration.setName(importName);
			importDeclaration.setOnDemand(true); // I think this is the equivalent of putting '*' on the end of the import.
			_moduleUnit.imports().add(importDeclaration);		
		}
		
		// create primary type
		File lohrFile= _lohrUnit.getFile();
		if (lohrFile == null)
			throw LohrCompilationError.MissingFile(_lohrUnit);
		TypeDeclaration moduleType= _ast.newTypeDeclaration();
		SimpleName moduleTypeName;
		{ 
			String filename= lohrFile.getName();
			// remove file extension
			int i= filename.indexOf('.');
			if (0 <= i)
				filename= filename.substring(0, i);
			moduleTypeName= _ast.newSimpleName(filename+"$$Module");
		}
		moduleType.setName(moduleTypeName);
		Name lohrModuleTypeName= _ast.newName("lohr.lang.impl.LohrModule");
		moduleType.setSuperclassType(_ast.newSimpleType(lohrModuleTypeName));
		_moduleUnit.types().add(moduleType);
		
		// create static initializer like this:
		// 		static {
		//			new WorkerModule().initialize();
		// 		}
		{
			Initializer initializer= _ast.newInitializer();
			initializer.modifiers().add(Modifier.STATIC);
			Block body= _ast.newBlock();
			initializer.setBody(body);
			ClassInstanceCreation instanceCreation= _ast.newClassInstanceCreation();
			instanceCreation.setType(_ast.newSimpleType(moduleTypeName));
			org.eclipse.jdt.core.dom.MethodInvocation methodInvocation= _ast.newMethodInvocation();
			methodInvocation.setExpression(instanceCreation);
			methodInvocation.setName(_ast.newSimpleName("initialize"));
			body.statements().add(methodInvocation);
			moduleType.bodyDeclarations().add(initializer);
		}
		
		
		MethodDeclaration initMethodDeclaration= _ast.newMethodDeclaration();
		initMethodDeclaration.setReturnType2(_ast.newPrimitiveType(PrimitiveType.VOID));
		

		return true;
	}

}
